package com.hanggold.captcha;

import com.hanggold.captcha.common.CaptchaConstant;
import com.hanggold.captcha.pojo.CaptchaEntity;
import com.hanggold.captcha.util.FontFamilyUtil;
import com.madgag.gif.fmsware.AnimatedGifEncoder;
import lombok.SneakyThrows;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_COUNT;
import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_FONT_SIZE;
import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_GIF_COUNT;
import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_GIT_DELAY_TIME;
import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_HEIGHT;
import static com.hanggold.captcha.common.CaptchaConstant.CAPTCHA_WEIGHT;
import static com.hanggold.captcha.common.CaptchaConstant.DEFAULT_VALUE;
import static com.hanggold.captcha.common.CaptchaConstant.DISTURB_CIRCLE_COUNT;
import static com.hanggold.captcha.common.CaptchaConstant.DISTURB_CIRCLE_HEIGHT;
import static com.hanggold.captcha.common.CaptchaConstant.DISTURB_CIRCLE_WEIGHT;
import static com.hanggold.captcha.common.CaptchaConstant.DISTURB_LINES_COUNT;
import static com.hanggold.captcha.util.RandomUtil.randomInt;
import static java.awt.image.BufferedImage.TYPE_INT_RGB;

/**
 * captcha.
 *
 * @author <a href="mailto:free@hanggold.com">free</a>
 * @date 2021-09-29 21:08
 * @since 1.0.0
 **/
public interface Captcha {
    
    /**
     * base64图片及验证码值.
     *
     * @return 验证码图片base64及验证码值
     */
    CaptchaEntity base64();
    
    /**
     * 写入内容流,返回验证码值.
     *
     * @param outputStream outputStream
     * @return string
     */
    String write(OutputStream outputStream);
    
    /**
     * 需要生成验证码的字符.
     *
     * @return 验证码字符
     */
    default char[] alphas() {
        return DEFAULT_VALUE.toCharArray();
    }
    
    /**
     * 返回一个标准的字符作为宽度计算需要.
     *
     * @return string
     */
    default String standardWeightChar() {
        return "W";
    }
    
    /**
     * 默认图片类型.
     *
     * @return 图片类型
     */
    default int imageType() {
        return TYPE_INT_RGB;
    }
    
    /**
     * 画干扰线.
     *
     * @param count count
     * @param width width
     * @param height height
     * @param graphics2D graphics2D
     */
    default void drawInterferenceLines(int count, int width, int height, Graphics2D graphics2D) {
        for (int i = 0; i <= randomInt(count); i++) {
            graphics2D.setColor(color());
            graphics2D.setStroke(new BasicStroke(1.2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
            QuadCurve2D shape = new QuadCurve2D.Double();
            shape.setCurve(0, randomInt(height), randomInt(20), randomInt(20), width, randomInt(height));
            graphics2D.draw(shape);
        }
    }
    
    /**
     * 画干扰圆.
     *
     * @param count count
     * @param graphics2D graphics2D
     * @param canvasWidth canvasWidth
     * @param canvasHeight canvasHeight
     */
    default void drawInterferenceCircle(int count, Graphics2D graphics2D, int canvasWidth, int canvasHeight) {
        int circleCount = randomInt(1, count);
        int everyBlockWidth = canvasWidth / circleCount;
        for (int i = 0; i <= circleCount; i++) {
            int x = randomInt(everyBlockWidth * i + circleWeight() / 2, everyBlockWidth * i + (everyBlockWidth - circleWeight() / 2));
            int y = randomInt(canvasHeight - circleHeight());
            graphics2D.setColor(color());
            graphics2D.drawOval(x, y, circleWeight(), circleHeight());
        }
        
    }
    
    /**
     * 生成验证码图片.
     *
     * @param out outputStream
     * @param standardWeightChar 目标宽度字符
     * @param width 验证码宽度
     * @param height 验证码高度
     * @param imageType 图片类型
     * @param contents 验证码内容
     * @throws IOException ioException
     */
    default void buildImage(final OutputStream out, final String standardWeightChar, final int width, final int height,
                            final int imageType, final char[] contents) throws IOException {
        BufferedImage bufferedImage = getBufferedImage(standardWeightChar, width, height, imageType, contents);
        ImageIO.write(bufferedImage, imageFormatType(), out);
    }
    
    /**
     * 生成BufferedImage.
     *
     * @param standardWeightChar 目标宽度字符
     * @param width 验证码宽度
     * @param height 验证码高度
     * @param imageType 图片类型
     * @param contents 验证码内容
     * @return BufferedImage
     */
    default BufferedImage getBufferedImage(final String standardWeightChar, final int width, final int height, final int imageType,
                                           final char[] contents) {
        BufferedImage bufferedImage = new BufferedImage(width, height, imageType);
        Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
        graphics.setColor(bgColor());
        graphics.fillRect(0, 0, width, height);
        drawInterferenceLines(lineCount(), width, height, graphics);
        drawInterferenceCircle(circleCount(), graphics, width, height);
        graphics.setFont(font());
        FontMetrics fontMetrics = graphics.getFontMetrics();
        int everyBlockWidth = width / contents.length;
        int fSp = (everyBlockWidth - (int) fontMetrics.getStringBounds(standardWeightChar, graphics).getWidth()) / 2;
        for (int i = 0; i < contents.length; i++) {
            graphics.setColor(color());
            int hc = (int) fontMetrics.getStringBounds(String.valueOf(contents[i]), graphics).getHeight();
            int captchaHeight = (height - hc) / 2;
            int hh = randomInt(captchaHeight);
            graphics.drawString(String.valueOf(contents[i]), i * everyBlockWidth + fSp, hh + hc);
        }
        graphics.dispose();
        return bufferedImage;
    }
    
    /**
     * 生成gif图片.
     *
     * @param out  输出流
     * @param standardWeightChar 目标宽度字符
     * @param width 验证码宽度
     * @param height 验证码高度
     * @param imageType 图片类型
     * @param contents 验证码内容
     */
    default void buildGif(final OutputStream out, final String standardWeightChar, final int width, final int height, 
                          final int imageType, final char[] contents) {
        AnimatedGifEncoder animatedGifEncoder = new AnimatedGifEncoder();
        animatedGifEncoder.setSize(width, height);
        animatedGifEncoder.start(out);
        animatedGifEncoder.setDelay(CAPTCHA_GIT_DELAY_TIME);
        animatedGifEncoder.setRepeat(0);
        for (int i = 0; i < CAPTCHA_GIF_COUNT; i++) {
            BufferedImage bufferedImage = getBufferedImage(standardWeightChar, width, height, imageType, contents);
            animatedGifEncoder.addFrame(bufferedImage);
            bufferedImage.flush();
        }
        animatedGifEncoder.finish();
    }
    
    /**
     * 图片类型名称.
     *
     * @return string
     */
    default String imageFormatType() {
        return "png";
    }
    
    /**
     * 干扰线数量.
     *
     * @return int
     */
    default int lineCount() {
        return DISTURB_LINES_COUNT;
    }
    
    /**
     * 干扰圆数量.
     *
     * @return int
     */
    default int circleCount() {
        return DISTURB_CIRCLE_COUNT;
    }
    
    /**
     * 生成颜色.
     *
     * @return color
     */
    default Color color() {
        int colorNum = randomInt(CaptchaConstant.COLOR.length);
        Color color = new Color(CaptchaConstant.COLOR[colorNum][0], CaptchaConstant.COLOR[colorNum][1], CaptchaConstant.COLOR[colorNum][2]);
        return color;
    }
    
    /**
     * 字体大小.
     *
     * @return font
     */
    @SneakyThrows
    default Font font() {
        try (InputStream inputStream = FontFamilyUtil.getInstance().inputStream()) {
            Font font = Font.createFont(Font.TRUETYPE_FONT, inputStream).deriveFont(Float.valueOf(String.valueOf(fontSize())));
            return font;
        }
    }
    
    /**
     * 验证码字符串数量.
     *
     * @return int
     */
    default int count() {
        return CAPTCHA_COUNT;
    }
    
    /**
     * 验证码宽度.
     *
     * @return int
     */
    default int weight() {
        return CAPTCHA_WEIGHT;
    }
    
    /**
     * 验证码高度.
     *
     * @return int
     */
    default int height() {
        return CAPTCHA_HEIGHT;
    }
    
    /**
     * 画布背景颜色.
     *
     * @return color
     */
    default Color bgColor() {
        return Color.white;
    }
    
    /**
     * 干扰圆宽度.
     *
     * @return int
     */
    default int circleWeight() {
        return DISTURB_CIRCLE_WEIGHT;
    }
    
    /**
     * 干扰圆高度.
     *
     * @return int
     */
    default int circleHeight() {
        return DISTURB_CIRCLE_HEIGHT;
    }
    
    /**
     * 字体size.
     *
     * @return int
     */
    default int fontSize() {
        return CAPTCHA_FONT_SIZE;
    }
    
}
